#include "PreCompiledHeader.h"
#include "GameState_Asteroid.hpp"
#include "System.hpp"
#include "Renderer.hpp"
#include "Collision.h"
#include "Vector2D.h"
#include "Client.h"
#include "Timer.h"
#include <string>
#include <sstream>


/******************************************************************************/
/*!
Defines
*/
/******************************************************************************/
const float PI =  float(3.1415926535897932384626433832795);

const unsigned int	GAME_OBJ_NUM_MAX		= 32;			//The total number of different objects (Shapes)



const unsigned int	GAME_OBJ_INST_NUM_MAX	= 15;			//The total number of different game object instances
const unsigned int	BULLET_OBJ_INST_NUM_MAX	= 5;			//The total number of different game object instances
const unsigned int	ASTEROID_OBJ_INST_NUM_MAX	= 10;			//The total number of different game object instances

const unsigned int	NUM_OF_SHIP				= 4;
const unsigned int	SHIP_INITIAL_NUM		= 3;			// initial number of ship lives
const float			SHIP_SIZE				= 30.0f;			// ship size

const float			SHIP_MAX_SPEED			    = 50.0f;		// Maximum speed (m/s)
const Vector2D      BOUNDING_RECT_SIZE      ( 1.0f, 1.0f ); //Size of the bounding rectangle


const float			SHIP_ACCEL_FORWARD		= 40.0f;		// ship forward acceleration (in m/s^2)
const float			SHIP_ACCEL_BACKWARD		= -40.0f;		// ship backward acceleration (in m/s^2)
const float			SHIP_ROT_SPEED			= (3.0f * PI);	// ship rotation speed (degree/second)

const float			ASTEROID_SIZE     = 30.0f;
const float			BULLET_SPEED			= 80.0f;		// bullet speed (m/s)
const float	        BULLET_SIZE             = 10.0f;			//Size of the bullet      
const int           MAX_ASTEROID_ONSCREEN   = 8;            // Maximum number of Aestroid onscreen  


#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)


// -----------------------------------------------------------------------------
enum TYPE
{
	// list of game object types

	TYPE_SHIP = 0, 
	TYPE_ASTEROID,
	TYPE_BULLET,
	TYPE_BACKGROUND
};


// -----------------------------------------------------------------------------
// object flag definition
const unsigned long FLAG_ACTIVE				= 0x00000001;

/******************************************************************************/
/*!
Struct/Class Definitions
*/
/******************************************************************************/

//Game object structure
struct GameObj
{
	unsigned long		type;		// object type

};

// ---------------------------------------------------------------------------

//Game object instance structure
struct GameObjInst
{
	GameObj*			pObject;	// pointer to the 'original' shape
	bool isVisible;
	unsigned			flag;		// bit flag or-ed together
	float				scale;		// scaling value of the object instance
	Vec2				posCurr;	// object current position
	Vec2				velCurr;	// object current velocity
	float				dirCurr;	// object current direction
	AABB				boundingBox;// object bouding box that encapsulates the object
	D3DXMATRIX			transform;	// object transformation matrix: Each frame, 
	// calculate the object instance's transformation matrix and save it here

	unsigned			shipNm;
	// number of ship available (lives 0 = game over)
	int			sShipLives;									// The number of lives left

	unsigned			surviveTime;
	//void				(*pfUpdate)(void);
	//void				(*pfDraw)(void);
};

float fRand(float start, float end);

/******************************************************************************/
/*!
Static Variables
*/
/******************************************************************************/

// list of original object
static GameObj				sGameObjList[GAME_OBJ_NUM_MAX];				// Each element in this array represents a unique game object (shape)

static unsigned long		sGameObjNum;								// The number of defined game objects

// list of object instances
static GameObjInst			sGameObjInstList[GAME_OBJ_INST_NUM_MAX];	// Each element in this array represents a unique game object instance (sprite)
//static unsigned long		sGameObjInstNum;							// The number of used game object instances

// list of Bullet instances
static GameObjInst			sBulletObjInstList[NUM_OF_SHIP][BULLET_OBJ_INST_NUM_MAX];
//static unsigned long		sBulletObjInstNum;							// The number of used game object instances


//list of Asteroid instances
static GameObjInst			sAsteroidObjInstList[ASTEROID_OBJ_INST_NUM_MAX];


// pointer ot the ship object
static GameObjInst*			spShip;										// Pointer to the "Ship" game object instance


// the score = number of asteroid destroyed
static unsigned long		sScore;										// Current score

// Flag of the the aestroid. True if there is none. 
static bool NoAsteroid= true; 

//Spawnpoint of the Aestroid
static Vector2D				SpawnPoints[4]; 

static double bulletTime;
static double gameTIme;
static double shipAliveNum = 4;
// Pointer to Client networking object
static Client* pClient;
static int gShipNum = 0;
int dataType = INVALID;
// ---------------------------------------------------------------------------

// functions to create/destroy a game object instance
static GameObjInst*			gameObjInstCreate (unsigned long type, float scale, 
	Vec2* pPos, Vec2* pVel, float dir, unsigned int shipNum =0);
static void					gameObjInstDestroy(GameObjInst* pInst);
bool CreateNewClient(int& type);
ClientTask PrepareData(int type, int orientation);
void UpdateFromServerData();
void LoadFromServerData();

/******************************************************************************/
/*!
"Load" function of this state
*/
/******************************************************************************/
void GameStateAsteroidsLoad(void)
{
	// Allocates memory for a new client
	CreateNewClient(gShipNum); // 3-way handshake. Receives ship number from the server

	// zero the game object array
	memset(sGameObjList, 0, sizeof(GameObj) * GAME_OBJ_NUM_MAX);
	// No game objects (shapes) at this point
	sGameObjNum = 0;

	// zero the game object instance array
	memset(sGameObjInstList, 0, sizeof(GameObjInst) * GAME_OBJ_INST_NUM_MAX);

	//Zero the game object instance array
	memset(sBulletObjInstList, 0, sizeof(GameObjInst) * BULLET_OBJ_INST_NUM_MAX);

	//Zero the game object instance array
	memset(sAsteroidObjInstList, 0, sizeof(GameObjInst) * ASTEROID_OBJ_INST_NUM_MAX);

	// The ship object instance hasn't been created yet, so this "spShip" pointer is initialized to 0
	spShip = 0;

	// load/create the mesh data (game objects / Shapes)
	GameObj* pObj;

	// =====================
	// create the ship 
	// =====================
	pObj		= sGameObjList + sGameObjNum++;
	pObj->type	= TYPE_SHIP;

	// =========================
	// create the asteroid 
	// =========================
	pObj		= sGameObjList + sGameObjNum++;
	pObj->type	= TYPE_ASTEROID;

	// =======================
	// create the bullet 
	// =======================
	pObj		= sGameObjList + sGameObjNum++;
	pObj->type	= TYPE_BULLET;

	//create the background
	pObj		= sGameObjList + sGameObjNum++;
	pObj->type	= TYPE_BACKGROUND;

	GameObjInst* pInst; 
	
	// Background
	pInst = gameObjInstCreate(TYPE_BACKGROUND, 900, 0, 0, 0.0f);

	// create the first ship
	Vec2 pos, vel;
	pos.x = -50.0f;	pos.y = 50.0f;
	pInst = gameObjInstCreate(TYPE_SHIP, SHIP_SIZE, &pos, 0, 1);
	pInst->shipNm = 1;
	pInst->isVisible = false;

	// create the second ship
	pos.x = 50.0f;	pos.y = 50.0f;
	pInst = gameObjInstCreate(TYPE_SHIP, SHIP_SIZE, &pos, 0, 0, 2);
	pInst->shipNm = 2;
	pInst->isVisible = false;

	// create the third ship
	pos.x = -50.0f;	pos.y = -50.0f;
	pInst = gameObjInstCreate(TYPE_SHIP, SHIP_SIZE, &pos, 0, 0, 3);
	pInst->shipNm = 3;
	pInst->isVisible = false;

	// create the fourth ship
	pos.x = 50.0f;	pos.y = -50.0f;
	pInst = gameObjInstCreate(TYPE_SHIP, SHIP_SIZE, &pos, 0, 0, 4);
	pInst->shipNm = 4;
	pInst->isVisible = false;

	// Assign player to his ship
	spShip = &sGameObjInstList[gShipNum];

	// CREATE THE INITIAL ASTEROIDS INSATNCES USING THE "GAMEOBJINSTCREATE" FUNCTION
	//4 initial asteroids instances with defined positions and velocities.
	pos.x = 50.0f;	pos.y = 100.0f;
	vel.x = 40.0f;	vel.y = 20.0f;
	pInst = gameObjInstCreate(TYPE_ASTEROID, 30.0f, &pos, &vel, 0.0f);
	pInst->isVisible = false;

	pos.x = -100.0f; pos.y = -100.0f;
	vel.x = -30.0f;	vel.y = 10.0f;
	pInst = gameObjInstCreate(TYPE_ASTEROID, 25.0f, &pos, &vel, 0.0f);
	pInst->isVisible = false;

	pos.x = 100.0f;	pos.y = -100.0f;
	vel.x = 15.0f;	vel.y = -15.0f;
	pInst = gameObjInstCreate(TYPE_ASTEROID, 30.0f, &pos, &vel, 0.0f);
	pInst->isVisible = false;

	pos.x = -100.0f; pos.y = 100.0f;
	vel.x = -8.0f;	vel.y = -12.0f;
	pInst = gameObjInstCreate(TYPE_ASTEROID, 25.0f, &pos, &vel, 0.0f);
	pInst->isVisible = false;

	// reset the score and the number of ship
	sScore      = 0;

	SpawnPoints[0].x= rand() % SYSTEM->GetWindowWidth() - SYSTEM->GetWindowWidth()/2; 
	SpawnPoints[0].y= SYSTEM->GetWindowHeight()/2 + SHIP_SIZE/3;

	SpawnPoints[1].x= rand() % SYSTEM->GetWindowWidth() - SYSTEM->GetWindowWidth()/2; 
	SpawnPoints[1].y= -SYSTEM->GetWindowHeight()/2 - SHIP_SIZE/3;;

	SpawnPoints[2].x= SYSTEM->GetWindowWidth()/2 + SHIP_SIZE/3;;
	SpawnPoints[2].y= rand() % SYSTEM->GetWindowHeight()/2 - SYSTEM->GetWindowHeight()/2;

	SpawnPoints[3].x= -SYSTEM->GetWindowWidth()/2 - SHIP_SIZE/3;;
	SpawnPoints[3].y= rand() % SYSTEM->GetWindowHeight()/2 - SYSTEM->GetWindowHeight()/2;

	for(unsigned i = 1; i < 5; ++i)
		for(unsigned j = 0; j < 5; ++j)
		{
			//GameObjInst* pInst = gameObjInstCreate(TYPE_BULLET, BULLET_SIZE, NULL, NULL, 0, i);
		//for (unsigned long i = 0; i < BULLET_OBJ_INST_NUM_MAX; i++)
		//{
			GameObjInst* pInst = &sBulletObjInstList[i - 1][j];

			// check if current instance is not used
			//if (pInst->flag == 0)
			//{
				// it is not used => use it to create the new instance
				pInst->pObject	= sGameObjList + TYPE_BULLET;
				pInst->flag		= FLAG_ACTIVE;
				pInst->scale		= BULLET_SIZE;
				pInst->posCurr	= Vector2D(0,0);
				pInst->velCurr	= Vector2D(0,0);
				pInst->dirCurr	= 0;
			//	pInst->sShipLives = 100;
				pInst->isVisible = true;
				// return the newly created instance
			//	return pInst;
		//	}
		//}			
			
			
			pInst->flag = 0;
			//pInst->isVisible = true;
		}

	LoadFromServerData();
}

/******************************************************************************/
/*!
	"Initialize" function of this state
*/
/******************************************************************************/
void GameStateAsteroidsInit(void)
{
	pClient->m_startIdleTimer = pClient->m_clientTimer->GetTime();
}

/******************************************************************************/
/*!
"Update" function of this state
*/
/******************************************************************************/
void GameStateAsteroidsUpdate(void)
{
	bool inputPressed = false;
	int orientation = UP;
	float AccelerateDeccelerate = 0.0f;
	
	UpdateFromServerData(); // Update object info from server data

	// ======================================================
	// update and limit the Velocity of the Ship 
	// ======================================================
	//Velocity of the ship according to the acceleration.
	if (KEY_DOWN(VK_ESCAPE))
	{
		gGameStateNext = GS_MAINMENU; 
	}

	Vec2 added(cosf(spShip->dirCurr), sinf(spShip->dirCurr));

	if (KEY_DOWN(VK_UP))
	{
		ClientTask data = PrepareData(MOVE, UP); // Updates player's ship data to prepare to send it
		if(!pClient->Send(reinterpret_cast<const char*>(&data))) // Sends player info to the server
		{
		}

		SecureZeroMemory(&data, sizeof(ClientTask));
	}

	if(spShip->isVisible && KEY_DOWN(VK_LEFT))
	{
		pClient->m_readyToSend = true;
		dataType = MOVE;
		orientation = LEFT;
		ClientTask data = PrepareData(MOVE, LEFT); // Updates player's ship data to prepare to send it
		if(!pClient->Send(reinterpret_cast<const char*>(&data))) // Sends player info to the server
		{


		}

		SecureZeroMemory(&data, sizeof(ClientTask));
	}

	if (spShip->isVisible && KEY_DOWN(VK_RIGHT))
	{
		pClient->m_readyToSend = true;
		dataType = MOVE;
		orientation = RIGHT;
		ClientTask data = PrepareData(MOVE, RIGHT); // Updates player's ship data to prepare to send it
		if(!pClient->Send(reinterpret_cast<const char*>(&data))) // Sends player info to the server
		{
			std::cout << "Failed to run" << std::endl;
		}

		SecureZeroMemory(&data, sizeof(ClientTask));
	}

	// Shoot a bullet if space is triggered (Create a new object instance)
	bulletTime +=  Timer::GetInstance()->GetDeltaTime();

	if (spShip->isVisible && KEY_DOWN(VK_SPACE) && (bulletTime) > 0.1f )
	{			
		pClient->m_readyToSend = true;
		dataType = FIRE;
		orientation = UP;
		ClientTask data = PrepareData(FIRE, UP); // Updates player's ship data to prepare to send it
		if(!pClient->Send(reinterpret_cast<const char*>(&data))) // Sends player info to the server
		{
			std::cout << "Failed to run" << std::endl;
		}

		SecureZeroMemory(&data, sizeof(ClientTask));
	}

	// ======================================================
	// update physics of all active game object instances
	//	-- Positions are updated here with the computed velocity
	//  -- Get the bouding rectangle of every active instance:
	//		boundingRect_min = -BOUNDING_RECT_SIZE * instance->scale + instance->pos
	//		boundingRect_max = BOUNDING_RECT_SIZE * instance->scale + instance->pos
	// ======================================================

	// Update ship wrapping and bounding box
	//for (unsigned long i = 1; i < 5; i++)
	//{
	//	GameObjInst* pInst = &sGameObjInstList[i];
	//	// skip non-active object
	//	if ((pInst->flag & FLAG_ACTIVE) == 0)
	//		continue;

	//	//Bouding Box of every active instance
	//	pInst->boundingBox.min= pInst->posCurr - (BOUNDING_RECT_SIZE * pInst->scale * 0.5f);
	//	pInst->boundingBox.max= pInst->posCurr + (BOUNDING_RECT_SIZE * pInst->scale * 0.5f);

	//}

	// Update Bullet bounding box and wrapping
	//for (unsigned long j = 0; j < NUM_OF_SHIP; j++)
	//{
	//	for (unsigned long i = 0; i < BULLET_OBJ_INST_NUM_MAX; i++)
	//	{
	//		GameObjInst* pInst = &sBulletObjInstList[j][i];
	//		// skip non-active object
	//		if ((pInst->flag & FLAG_ACTIVE) == 0)
	//		{
	//			continue;
	//		}

	//		//Bouding Box of every active instance
	//		pInst->boundingBox.min= pInst->posCurr - (BOUNDING_RECT_SIZE * pInst->scale * 0.5f);
	//		pInst->boundingBox.max= pInst->posCurr + (BOUNDING_RECT_SIZE * pInst->scale * 0.5f);

	//		//If Out of Bound
	//		if(	pInst->posCurr.x < -SYSTEM->GetWindowWidth()/2 - SHIP_SIZE/2 ||
	//			pInst->posCurr.x > SYSTEM->GetWindowWidth()/2 + SHIP_SIZE/2 ||
	//			pInst->posCurr.y < -SYSTEM->GetWindowHeight()/2 - SHIP_SIZE/2 ||
	//			pInst->posCurr.y >  SYSTEM->GetWindowHeight()/2 +SHIP_SIZE/2 )
	//		{
	//			gameObjInstDestroy(pInst);
	//		}
	//	}
	//}

	//Update Asteroid bounding box and wrapping
	//for (unsigned long i = 0; i < ASTEROID_OBJ_INST_NUM_MAX; i++)
	//{
	//	GameObjInst* pInst = &sAsteroidObjInstList[i];
	//	// skip non-active object
	//	if ((pInst->flag & FLAG_ACTIVE) == 0)
	//		continue;
 //
	//	//Bouding Box of every active instance
	//	pInst->boundingBox.min= pInst->posCurr - (BOUNDING_RECT_SIZE * pInst->scale * 0.5f);
	//	pInst->boundingBox.max= pInst->posCurr + (BOUNDING_RECT_SIZE * pInst->scale * 0.5f);

	//	if(pInst->posCurr.x > SYSTEM->GetWindowWidth()/2 + SHIP_SIZE/2)
	//	{
	//		pInst->posCurr.x = -float(SYSTEM->GetWindowWidth())/2;
	//	}
	//	else if(pInst->posCurr.x < -SYSTEM->GetWindowWidth()/2 - SHIP_SIZE/2)
	//	{
	//		pInst->posCurr.x = float(SYSTEM->GetWindowWidth())/2;
	//	}
	//	if(pInst->posCurr.y > SYSTEM->GetWindowHeight()/2 + SHIP_SIZE/2)
	//	{
	//		pInst->posCurr.y = -float(SYSTEM->GetWindowHeight())/2;
	//	}
	//	else if(pInst->posCurr.y < -SYSTEM->GetWindowHeight()/2 -SHIP_SIZE/2)
	//	{
	//		pInst->posCurr.y = float(SYSTEM->GetWindowHeight())/2;
	//	}
	//}

	// ======================================================
	// check for collision
	// ======================================================
	//For each object instance: oi1
	//	if oi1 is not active
	//		skip
	//
	//	if oi1 is an asteroid
	//		for each object instance oi2
	//			if(oi2 is not active or oi2 is an asteroid)
	//				skip
	//
	//			if(oi2 is the ship)
	//				Check for collision between ship and asteroids (Rectangle - Rectangle)
	//				Update game behavior accordingly
	//				Update "Object instances array"
	//			else
	//			if(oi2 is a bullet)
	//				Check for collision between ship and asteroids (Rectangle - Rectangle)
	//				Update game behavior accordingly
	//				Update "Object instances array"
	// ======================================================
	//Asteroid Collision
	for (unsigned long counter = 0; counter < ASTEROID_OBJ_INST_NUM_MAX; ++counter)
	{
		GameObjInst* pInst = sAsteroidObjInstList + counter;

		// skip non-active object
		if ((pInst->flag & FLAG_ACTIVE) == 0)
			continue;

			for (unsigned long counter1 = 0; counter1 < GAME_OBJ_INST_NUM_MAX; ++counter1)
			{
				GameObjInst* pInst1 = sGameObjInstList + counter1;
				
				if ((pInst1->flag & FLAG_ACTIVE) == 0 )
					continue;

				else if(pInst1->pObject->type == TYPE_SHIP)
				{
					//If SHIP and Aesteroid collide
					if( CollisionIntersection_RectRect(pInst->boundingBox, pInst->velCurr, 
						pInst1->boundingBox, pInst1->velCurr) )
					{	
						(pInst1->sShipLives) -= 20;
						gameObjInstDestroy(pInst);
					}
				}

			}
			for (unsigned long j = 0; j < NUM_OF_SHIP; j++)
			{
				for (unsigned long counter1 = 0; counter1 < BULLET_OBJ_INST_NUM_MAX; counter1++)
				{
					GameObjInst* pInst1 = sBulletObjInstList[j] + counter1;
					if ((pInst1->flag & FLAG_ACTIVE) == 0 )
					{
						continue;
					}
					if(pInst1->pObject->type == TYPE_BULLET)
					{
						if( CollisionIntersection_RectRect(pInst->boundingBox, pInst->velCurr, 
							pInst1->boundingBox, pInst1->velCurr) )
						{	
							++sScore;
							gameObjInstDestroy(pInst1);
							gameObjInstDestroy(pInst);
						}
					}
				}
			}
	}

	for (unsigned long j = 0; j < NUM_OF_SHIP; j++)
	{
		for (unsigned long counter = 0; counter < BULLET_OBJ_INST_NUM_MAX; ++counter)
		{
			GameObjInst* pInst = sBulletObjInstList[j] + counter;

			// skip non-active object
			if ((pInst->flag & FLAG_ACTIVE) == 0)
				continue;

			if (pInst->pObject->type == TYPE_BULLET)
			{
				for (unsigned long counter1 = 0; counter1 < GAME_OBJ_INST_NUM_MAX; counter1++)
				{
					GameObjInst* pInst1 = sGameObjInstList + counter1;
					if ((pInst1->flag & FLAG_ACTIVE) == 0)
						continue;

					else if(pInst1->pObject->type == TYPE_SHIP && pInst1->shipNm != gShipNum)
					{
						//If SHIP and Aesteroid collide
						if( CollisionIntersection_RectRect(pInst->boundingBox, pInst->velCurr, 
							pInst1->boundingBox, pInst1->velCurr) )
						{	
							--(pInst1->sShipLives);
							//	gameObjInstDestroy(pInst);
						}
					}
				}
			}
		}
	}

	// =====================================
	// calculate the matrix for all objects
	// =====================================
	for (unsigned long i = 0; i < GAME_OBJ_INST_NUM_MAX; i++)
	{
		GameObjInst* pInst = sGameObjInstList + i;
		D3DXMATRIX		 trans, rot, scale;

		// skip non-active object
		if ((pInst->flag & FLAG_ACTIVE) == 0)
			continue;

		// Compute the scaling matrix
		D3DXMatrixScaling(&scale, pInst->scale, pInst->scale,1.0f);
		D3DXMatrixRotationZ(&rot,pInst->dirCurr );
		D3DXMatrixTranslation(&trans,pInst->posCurr.x,pInst->posCurr.y,1);

		if(pInst->pObject->type == TYPE_BACKGROUND)
			D3DXMatrixTranslation(&trans,pInst->posCurr.x,pInst->posCurr.y,2);

		// Concatenate the 3 matrix in the correct order in the object instance's "transform" matrix
		pInst->transform= scale * rot * trans ;
	}

	// =====================================
	// calculate the matrix for Bullets
	// =====================================
	for (unsigned long j = 0; j < NUM_OF_SHIP; j++)
	{
		for (unsigned long i = 0; i < BULLET_OBJ_INST_NUM_MAX; i++)
		{
			GameObjInst* pInst = sBulletObjInstList[j] + i;
			D3DXMATRIX		 trans, rot, scale;

			// skip non-active object
			if ((pInst->flag & FLAG_ACTIVE) == 0)
				continue;

			// Compute the scaling matrix
			D3DXMatrixScaling(&scale, pInst->scale, pInst->scale,1.0f);
			D3DXMatrixRotationZ(&rot,pInst->dirCurr );
			D3DXMatrixTranslation(&trans,pInst->posCurr.x,pInst->posCurr.y,1);

			// Concatenate the 3 matrix in the correct order in the object instance's "transform" matrix
			pInst->transform= scale * rot * trans ;
		}
	}
	// =====================================
	// calculate the matrix for Asteroid
	// =====================================
	for (unsigned long i = 0; i < ASTEROID_OBJ_INST_NUM_MAX; i++)
	{
		GameObjInst* pInst = sAsteroidObjInstList + i;
		D3DXMATRIX		 trans, rot, scale;

		// skip non-active object
		if ((pInst->flag & FLAG_ACTIVE) == 0)
			continue;

		// Compute the scaling matrix
		D3DXMatrixScaling(&scale, pInst->scale, pInst->scale,1.0f);
		D3DXMatrixRotationZ(&rot,pInst->dirCurr );
		D3DXMatrixTranslation(&trans,pInst->posCurr.x,pInst->posCurr.y,1);

		// Concatenate the 3 matrix in the correct order in the object instance's "transform" matrix
		pInst->transform= scale * rot * trans ;
	}
}

float fRand(float start, float end)
{
	float multiplier = end - start;
	return (((float (rand())/RAND_MAX) * multiplier) + start);
}

/******************************************************************************/
/*!
Drawing of Aestroid onto buffer
*/
/******************************************************************************/
void GameStateAsteroidsDraw(void)
{
	//win condition
	SYSTEM->GetRenderer()->PrepareToDraw();
	char strBuffer[1024];

	//Draw Text
	double fps = Timer::GetInstance()->GetFPS();
	sprintf_s(strBuffer, "FPS: %4f", (fps));
	SYSTEM->GetRenderer()->PrintTextWorld( strBuffer, float(SYSTEM->GetWindowWidth())/2 - 800, 
		float(SYSTEM->GetWindowHeight())/2 -10, WHITE, 500, 10);

	memset(strBuffer, 0, 1024);

	// draw all object instances in the list
	for (unsigned long i = 0; i < GAME_OBJ_INST_NUM_MAX; i++)
	{
		GameObjInst* pInst = sGameObjInstList + i;

		// skip non-active object
		if (pInst->isVisible == false || ((pInst->flag & FLAG_ACTIVE) == 0))
			continue;

		// Set the current object instance's transform matrix using "AEGfxSetTransform"
		// Draw the shape used by the current object instance using "AEGfxTriDraw"
		//Draw selected object

		//AEGfxSetTransform((AEMtx33 *)&pInst->transform);
		//AEGfxTriDraw(pInst->pObject->pMesh);
	


		//draw background
		if(pInst->pObject->type == TYPE_BACKGROUND)
		{
			SYSTEM->GetRenderer()->DrawBackground(pInst->transform);
		}	

	
		if(shipAliveNum == 1 && pInst->sShipLives >0)
		{
			sprintf_s(strBuffer, "Player:%d Won The Game", pInst->shipNm);
			SYSTEM->GetRenderer()->PrintTextWorld( strBuffer, -100.f, -60.f, WHITE, 500, 10);
			memset(strBuffer, 0, 1024);
		}

		//draw ships
		if(pInst->pObject->type==TYPE_SHIP && pInst->shipNm == 1)
		{
			SYSTEM->GetRenderer()->DrawTexture(pInst->transform);
			sprintf_s(strBuffer, "Player 1 HP: %d", pInst->sShipLives);
			SYSTEM->GetRenderer()->PrintTextWorld( strBuffer, float(SYSTEM->GetWindowWidth())/2 - 200, 
										float(SYSTEM->GetWindowHeight())/2 -10, WHITE, 400, 10);
			memset(strBuffer, 0, 1024);
			if( pInst->sShipLives <= 0)
			{
				SYSTEM->GetRenderer()->PrintTextWorld( "You Have Lost The Game!", -100.f, 0.f, WHITE, 500, 10);
				--shipAliveNum;
			}
			memset(strBuffer, 0, 1024);
		}

		if(pInst->pObject->type == TYPE_SHIP && pInst->shipNm == 2)
		{
			SYSTEM->GetRenderer()->DrawTexture2(pInst->transform);
			sprintf_s(strBuffer, "Player 2 HP: %d", pInst->sShipLives);
			SYSTEM->GetRenderer()->PrintTextWorld( strBuffer, float(SYSTEM->GetWindowWidth())/2 - 200, 
										float(SYSTEM->GetWindowHeight())/2 -30, WHITE, 400, 10);
			memset(strBuffer, 0, 1024);
			if( pInst->sShipLives <= 0)
			{
				sprintf_s(strBuffer, "Player:%d Lost The Game", pInst->shipNm);
				SYSTEM->GetRenderer()->PrintTextWorld( strBuffer, -100.f, 0.f, WHITE, 500, 10);
				--shipAliveNum;
			}
			memset(strBuffer, 0, 1024);
		}
		if(pInst->pObject->type == TYPE_SHIP && pInst->shipNm == 3)
		{

			SYSTEM->GetRenderer()->DrawTexture3(pInst->transform);
			sprintf_s(strBuffer, "Player 3 HP: %d", pInst->sShipLives);
			SYSTEM->GetRenderer()->PrintTextWorld( strBuffer, float(SYSTEM->GetWindowWidth())/2 - 200, 
										float(SYSTEM->GetWindowHeight())/2 -50, WHITE, 400, 10);

			memset(strBuffer, 0, 1024);
			if( pInst->sShipLives <= 0)
			{
				sprintf_s(strBuffer, "Player:%d Lost The Game", pInst->shipNm);
				SYSTEM->GetRenderer()->PrintTextWorld( strBuffer, -100.f, 30.f, WHITE, 500, 10);
				--shipAliveNum;
			}
			memset(strBuffer, 0, 1024);
		}
		if(pInst->pObject->type == TYPE_SHIP && pInst->shipNm == 4)
		{
			SYSTEM->GetRenderer()->DrawTexture4(pInst->transform);
			sprintf_s(strBuffer, "Player 4 HP: %d", pInst->sShipLives);
			SYSTEM->GetRenderer()->PrintTextWorld( strBuffer, float(SYSTEM->GetWindowWidth())/2 - 200, 
										float(SYSTEM->GetWindowHeight())/2 -70, WHITE, 400, 10);
			memset(strBuffer, 0, 1024);
			if( pInst->sShipLives <= 0)
			{
				sprintf_s(strBuffer, "Player:%d Lost The Game", pInst->shipNm);
				SYSTEM->GetRenderer()->PrintTextWorld( strBuffer, -100.f, 60.f, WHITE, 500, 10);
				--shipAliveNum;
			}
			memset(strBuffer, 0, 1024);
		}
	}
	// draw all bullet instances in the list
	for (unsigned long j = 0; j < NUM_OF_SHIP; j++)
	{
	  for (unsigned long i = 0; i < BULLET_OBJ_INST_NUM_MAX; i++)
	  {
		  GameObjInst* pInst = &sBulletObjInstList[j][i];

		
		  // skip non-active object
		  if (pInst->isVisible = false || ((pInst->flag & FLAG_ACTIVE) == 0) || (!pInst->pObject))
			  continue;

		  if(pInst->pObject->type == TYPE_BULLET)
		  {
			  SYSTEM->GetRenderer()->DrawBulletTexture(pInst->transform);
		  }
	  }
	}

	// draw all Asteroid instances in the list
	for (unsigned long i = 0; i < ASTEROID_OBJ_INST_NUM_MAX; i++)
	{
		GameObjInst* pInst = sAsteroidObjInstList + i;

		// skip non-active object
		if ((pInst->flag & FLAG_ACTIVE) == 0)
			continue;

		if(pInst->isVisible = false || ((pInst->flag & FLAG_ACTIVE) == 0) || (pInst->pObject))
		{
			SYSTEM->GetRenderer()->DrawAstroid(pInst->transform);
		}

	}


	SYSTEM->GetRenderer()->EndDraw();
}

/******************************************************************************/
/*!
Free Asteroids
*/
/******************************************************************************/
void GameStateAsteroidsFree(void)
{
	// kill all object instances in the array using "gameObjInstDestroy"
	for(unsigned long i = 0; i < GAME_OBJ_INST_NUM_MAX; i++)
	{
		GameObjInst* pInst = sGameObjInstList + i;

		gameObjInstDestroy(pInst);
	}

	//Kill Bullet
	for (unsigned long j = 0; j < NUM_OF_SHIP; j++)
	{
		for(unsigned long i = 0; i < GAME_OBJ_INST_NUM_MAX; i++)
		{
			GameObjInst* pInst = sBulletObjInstList[j] + i;
			gameObjInstDestroy(pInst);
		}
	}

	//Kill Asteroid
	for(unsigned long i = 0; i < ASTEROID_OBJ_INST_NUM_MAX; i++)
	{
		GameObjInst* pInst = sAsteroidObjInstList + i;

		gameObjInstDestroy(pInst);
	}

}

/******************************************************************************/
/*!
Unload Asteroids
*/
/******************************************************************************/
void GameStateAsteroidsUnload(void)
{

}

/******************************************************************************/
/*!
Create Objects
*/
/******************************************************************************/
GameObjInst* gameObjInstCreate(unsigned long type, float scale, Vec2* pPos, 
	Vec2* pVel, float dir, unsigned int ShipNum)
{
	Vec2 zero(0.0f, 0.0f);
	switch(type)
	{
	case TYPE_SHIP:
		// loop through the object instance list to find a non-used object instance
		for (unsigned long i = 0; i < GAME_OBJ_INST_NUM_MAX; i++)
		{
			GameObjInst* pInst = sGameObjInstList + i;

			// check if current instance is not used
			if (pInst->flag == 0)
			{
				// it is not used => use it to create the new instance
				pInst->pObject	= sGameObjList + type;
				pInst->flag		= FLAG_ACTIVE;
				pInst->scale		= scale;
				pInst->posCurr	= pPos ? *pPos : zero;
				pInst->velCurr	= pVel ? *pVel : zero;
				pInst->dirCurr	= dir;
				pInst->sShipLives = 100;
				pInst->isVisible = true;
				// return the newly created instance
				return pInst;
			}
		}
		break;
	case TYPE_ASTEROID:
		// loop through the object instance list to find a non-used object instance
		for (unsigned long i = 0; i < ASTEROID_OBJ_INST_NUM_MAX; i++)
		{
			GameObjInst* pInst = sAsteroidObjInstList + i;

			// check if current instance is not used
			if (pInst->flag == 0)
			{
				// it is not used => use it to create the new instance
				pInst->pObject	= sGameObjList + type;
				pInst->flag		= FLAG_ACTIVE;
				pInst->scale		= scale;
				pInst->posCurr	= pPos ? *pPos : zero;
				pInst->velCurr	= pVel ? *pVel : zero;
				pInst->dirCurr	= dir;
				pInst->sShipLives = 100;
				pInst->isVisible = true;
				// return the newly created instance
				return pInst;
			}
		}

		break;
	//case TYPE_BULLET:
	//	// loop through the object instance list to find a non-used object instance
	//	for (unsigned long i = 0; i < BULLET_OBJ_INST_NUM_MAX; i++)
	//	{
	//		GameObjInst* pInst = &sBulletObjInstList[ShipNum - 1][i];

	//		// check if current instance is not used
	//		if (pInst->flag == 0)
	//		{
	//			// it is not used => use it to create the new instance
	//			pInst->pObject	= sGameObjList + type;
	//			pInst->flag		= FLAG_ACTIVE;
	//			pInst->scale		= scale;
	//			pInst->posCurr	= pPos ? *pPos : zero;
	//			pInst->velCurr	= pVel ? *pVel : zero;
	//			pInst->dirCurr	= dir;
	//			pInst->sShipLives = 100;
	//			pInst->isVisible = true;
	//			// return the newly created instance
	//			return pInst;
	//		}
	//	}
	//	break;

	case TYPE_BACKGROUND:
		for (unsigned long i = 0; i < GAME_OBJ_INST_NUM_MAX; i++)
		{
			GameObjInst* pInst = sGameObjInstList + i;

			// check if current instance is not used
			if (pInst->flag == 0)
			{
				// it is not used => use it to create the new instance
				pInst->pObject	= sGameObjList + type;
				pInst->flag		= FLAG_ACTIVE;
				pInst->scale		= scale;
				pInst->posCurr	= pPos ? *pPos : zero;
				pInst->velCurr	= pVel ? *pVel : zero;
				pInst->dirCurr	= dir;
				pInst->sShipLives = 100;
				pInst->isVisible = true;
				// return the newly created instance
				return pInst;
			}
		}
		break;

	}

	// cannot find empty slot => return 0
	return 0;
}


/******************************************************************************/
/*!
Destory Objects
*/
/******************************************************************************/
void gameObjInstDestroy(GameObjInst* pInst)
{
	// if instance is destroyed before, just return
	if (pInst->flag == 0)
		return;

	// zero out the flag
	pInst->flag = 0;
}

/******************************************************************************/
/*!
Creates a new client object
*/
/******************************************************************************/
bool CreateNewClient(int& type)
{
	// Create client side networking
	pClient = new Client("config.txt");
	std::cout << "Config file opened" << std::endl;
	if( !pClient->Setup() )
	{
		std::cout << "Failed to setup client" << std::endl;
		return 1;
	}
	// Send server my username and port number
	if( !pClient->SendUserName(type) )
	{
		std::cout << "Failed to send user name" << std::endl;
		return 1;
	}

	pClient->CreateReceivingThread();// Thread in charge of Receiving data to the server

	return true;
}

/******************************************************************************/
/*!
Prepares a game task struct to be sent to the server
Struct will contain player's information
*/
/******************************************************************************/
ClientTask PrepareData(int type, int orientation)
{
  ClientTask newTask;
  GameTask gameTask;
  if(type == MOVE)
  {
    newTask.type = MOVE;
    gameTask.type = orientation;
    gameTask.id = spShip->shipNm;
    std::memcpy(newTask.buffer, reinterpret_cast<char*>(&gameTask), sizeof(gameTask));
  }
  else if(type == FIRE)
  {
    newTask.type = MOVE;
    gameTask.type = FIRE;
    gameTask.id = spShip->shipNm;
    //gameTask.posX = spShip->posCurr.x;
    //gameTask.posY = spShip->posCurr.y;
    //gameTask.velX = spShip->velCurr.x;
    //gameTask.velY = spShip->velCurr.y;
    //gameTask.direction = spShip->dirCurr;
    //for(unsigned i = 0; i < 5; ++i)
    //{
      //if(sBulletObjInstList[spShip->shipNm][i].flag == 0)
      //{
        //gameTask.bulletVel = sBulletObjInstList[spShip->shipNm][i].velCurr;
        //gameTask.bulletPos = sBulletObjInstList[spShip->shipNm][i].posCurr;
      //}
    //}    
    std::memcpy(newTask.buffer, reinterpret_cast<char*>(&gameTask), sizeof(gameTask));
  }
  else // Disconnect
  {
  }


  return newTask;
}

void UpdateFromServerData()
{
	ServerGameStatus newGameStatus = pClient->CheckForEvents();

	// If there is an event from the server
	if(newGameStatus.type == GAMETASK)
	{
		//Update ships info
		for(unsigned long i = 1; i < 5; i++)
		{
			GameObjInst* pInst = &sGameObjInstList[i];

			//Update Ship's position
			pInst->posCurr.x = newGameStatus.playerX[i];
			pInst->posCurr.y = newGameStatus.playerY[i];
			pInst->dirCurr = newGameStatus.playerFacing[i];

			//Bouding Box of every active instance
			pInst->boundingBox.min= pInst->posCurr - (BOUNDING_RECT_SIZE * pInst->scale * 0.5f);
			pInst->boundingBox.max= pInst->posCurr + (BOUNDING_RECT_SIZE * pInst->scale * 0.5f);

			if(pInst->posCurr.x > SYSTEM->GetWindowWidth()/2 + SHIP_SIZE/2)
			{
				pInst->posCurr.x = -float(SYSTEM->GetWindowWidth())/2;
			}
			else if(pInst->posCurr.x < -SYSTEM->GetWindowWidth()/2 - SHIP_SIZE/2)
			{
				pInst->posCurr.x = float(SYSTEM->GetWindowWidth())/2;
			}
			if(pInst->posCurr.y > SYSTEM->GetWindowHeight()/2 + SHIP_SIZE/2)
			{
				pInst->posCurr.y = -float(SYSTEM->GetWindowHeight())/2;
			}
			else if(pInst->posCurr.y < -SYSTEM->GetWindowHeight()/2 -SHIP_SIZE/2)
			{
				pInst->posCurr.y = float(SYSTEM->GetWindowHeight())/2;
			}
		}

		// Update astroid instances based on info from server
		// i starts from 3 as players have already been updated
		for (unsigned long i = 0; i < ASTEROID_OBJ_INST_NUM_MAX; i++)
		{
			GameObjInst* pInst = &sAsteroidObjInstList[i];

			//Update asteroid's position
			pInst->posCurr.x = newGameStatus.asteroidsX[i];
			pInst->posCurr.y = newGameStatus.asteroidsY[i];
			pInst->flag = newGameStatus.asteroidFlag[i];

			//Bouding Box of every active instance
			pInst->boundingBox.min= pInst->posCurr - (BOUNDING_RECT_SIZE * pInst->scale * 0.5f);
			pInst->boundingBox.max= pInst->posCurr + (BOUNDING_RECT_SIZE * pInst->scale * 0.5f);

			if(pInst->posCurr.x > SYSTEM->GetWindowWidth()/2 + SHIP_SIZE/2)
			{
				pInst->posCurr.x = -float(SYSTEM->GetWindowWidth())/2;
			}
			else if(pInst->posCurr.x < -SYSTEM->GetWindowWidth()/2 - SHIP_SIZE/2)
			{
				pInst->posCurr.x = float(SYSTEM->GetWindowWidth())/2;
			}
			if(pInst->posCurr.y > SYSTEM->GetWindowHeight()/2 + SHIP_SIZE/2)
			{
				pInst->posCurr.y = -float(SYSTEM->GetWindowHeight())/2;
			}
			else if(pInst->posCurr.y < -SYSTEM->GetWindowHeight()/2 -SHIP_SIZE/2)
			{
				pInst->posCurr.y = float(SYSTEM->GetWindowHeight())/2;
			}

		}

		for (unsigned long j = 0; j < 4; j++)
		{
			for (unsigned long i = 0; i < BULLET_OBJ_INST_NUM_MAX; i++)
			{
				GameObjInst* pInst = &sBulletObjInstList[j][i];

				pInst->posCurr.x = newGameStatus.BulletX[j][i];
				pInst->posCurr.y = newGameStatus.BulletY[j][i];
				pInst->flag = newGameStatus.IsBullet[j][i];

				//Bouding Box of every active instance
				pInst->boundingBox.min= pInst->posCurr - (BOUNDING_RECT_SIZE * pInst->scale * 0.5f);
				pInst->boundingBox.max= pInst->posCurr + (BOUNDING_RECT_SIZE * pInst->scale * 0.5f);

				//If Out of Bound
				if(	pInst->posCurr.x < -SYSTEM->GetWindowWidth()/2 - SHIP_SIZE/2 ||
					pInst->posCurr.x > SYSTEM->GetWindowWidth()/2 + SHIP_SIZE/2 ||
					pInst->posCurr.y < -SYSTEM->GetWindowHeight()/2 - SHIP_SIZE/2 ||
					pInst->posCurr.y >  SYSTEM->GetWindowHeight()/2 +SHIP_SIZE/2 )
				{
					gameObjInstDestroy(pInst);
				}
			}
		}
	}
}



void LoadFromServerData()
{
	
	// If there is an event from the server
	while(true)//newGameStatus.type != INVALID)
	{
		ServerGameStatus newGameStatus = pClient->CheckForEvents();
		if(newGameStatus.type == BEGIN)
		{
			Vec2 pos;
			for(unsigned i = 1; i < 5; ++i)
			{
				GameObjInst* pInst = &sGameObjInstList[i];
				if(pInst->flag == FLAG_ACTIVE)
				{
					pInst->isVisible = true;
					std::cout << "ID: " << i << std::endl;
				}
			}

			for(unsigned i = 0; i < ASTEROID_OBJ_INST_NUM_MAX; ++i)
			{
				GameObjInst* pInst = &sAsteroidObjInstList[i];

				if(pInst->flag == FLAG_ACTIVE)
				{
					pInst->isVisible = true;
				}
			}
			break;
		}
	}
}
     